#include "BlockList.h"
#include "stdint.h"

void BlockList::initialize(uint8_t *buff, int width, int height, int totalBlocks, int columnAndRowSize)
{
	_matrixSize = 0;
	_columnLength = columnAndRowSize;
	_columns = width / _columnLength;
	_rows = height / _columnLength;
	_dataWidth = width;
	_dataHeight = height;
	_data = buff;
    initBlockList(totalBlocks, columnAndRowSize);
}

void BlockList::initBlockList(int totalBlocks, int columnAndRowSize)
{
	_matrixSize = columnAndRowSize * columnAndRowSize;
	_size = totalBlocks;
}

__host__ __device__ uint8_t BlockList::getPixel( int row, int column )
{
	int index = row * _dataWidth + column;

	return _data[index];
}


Matrix BlockList::operator[]( const int i )
{
	double* m = new double[_matrixSize];

	int row = i / _columns;
	int col = i % _columns;

	for(int j = 0; j < _columnLength; ++j)
	{
		for(int k = 0; k < _columnLength; ++k)
		{
			int base = _dataWidth * _columnLength * row + _columnLength * col;
			m[j*_columnLength + k] = _data[base + k + j*_dataWidth];
		}
	}

	Matrix mat(_columnLength);

	mat.setMatrixRowMajor(m);
	
	delete[] m;

	return mat;
}

Matrix BlockList::getBlockAtCoord(int x, int y)
{
	double* m = new double[_matrixSize];

    int k = 0;
    for(int i = x; i < x+_columnLength; ++i)
    {
        for(int j = y; j < y+_columnLength; ++j)
        {
            int base = j*_dataWidth+i;
            m[k] = _data[base];
            ++k;
        }
    }

	Matrix block(_columnLength, m);

	delete[] m;

    return block;
}

void BlockList::setDevice(int i, Matrix& block)
{	
	int row = i / _columns;
	int col = i % _columns;

	for(int j = 0; j < _columnLength; ++j)
	{
		for(int k = 0; k < _columnLength; ++k)
		{
			int base = _dataWidth * _columnLength * row + _columnLength * col;
			double temp = block.getRowMajorElement(j*_columnLength + k);
			int casted = (int)temp;
			char casted2 = (char)casted;
			unsigned char casted3 = (unsigned char)casted2;
			_data[base + k + j*_dataWidth] = casted3;
		}
	}
}

void BlockList::set(int i, Matrix& block)
{	
	int row = i / _columns;
	int col = i % _columns;

	for(int j = 0; j < _columnLength; ++j)
	{
		for(int k = 0; k < _columnLength; ++k)
		{
			int base = _dataWidth * _columnLength * row + _columnLength * col;
			double temp = block.getRowMajorElement(j*_columnLength + k);
			_data[base + k + j*_dataWidth] = temp;
		}
	}
}

void BlockList::clear()
{
	_data = NULL;
	_size = 0;
}


__host__ void BlockList::cloneToDevice(uint8_t** dest)
{
	size_t size = _size * _matrixSize * sizeof(uint8_t);

	cudaMalloc(dest, size);
	cudaMemcpy(*dest, _data, size, cudaMemcpyHostToDevice);
}

__host__ void BlockList::cloneToHost(uint8_t* src)
{
	size_t size = _size * _matrixSize * sizeof(uint8_t);
	cudaMemcpy(_data, src, size, cudaMemcpyDeviceToHost);

	if(src != NULL)
		cudaFree(src);
}
// 
// 
// __host__ void BlockList::clearDeviceMemory()
// {
// 
// }
// 
// __global__ void testData(Matrix* p)
// {
// 	double t = (*p)[0];
// 
// 	t += 1;
// }
// 
// __host__ void BlockList::testCUData()
// {
// 	Matrix* p = thrust::raw_pointer_cast(&_CUBlockList[0]);
// 
// 	testData<<<1,1>>>(p);
// }
